from tkinter import *
from tkinter import messagebox
from tkinter.scrolledtext import *

import time
import datetime
import os

from monitor import *


class MY_GUI():
    def __init__(self, init_window):
        self.init_window = init_window

        self.intervalVar = IntVar(value='60')   # 检测周期

        self.stock1Var = StringVar(value='159995')  # 实时监控
        self.rate1Var = DoubleVar(value=5.0)
        self.lowerVar = DoubleVar(value=10.0)
        self.upperVar = DoubleVar(value=10.0)

        self.stockVar = StringVar(value='159995')  # 周期MA监控
        self.periodVar = IntVar(value=30)
        self.maVar = IntVar(value=30)

        self.stock2Var = StringVar(value='159995')  # 趋势MA监控
        self.period2Var = IntVar(value=120)
        self.ma2Var = IntVar(value=30)
        self.rate2Var = DoubleVar(value=2.0)
        self.offset2Var = IntVar(value=1)

        self.init_window.protocol("WM_DELETE_WINDOW", self._on_close)

    def _on_close(self):
        """窗口退出事件处理"""
        if messagebox.askokcancel("Quit", "Do you want to quit?"):
            if self.monitor is not None:
                self.monitor.stop()
                self.monitor.save_to_config()
            self.init_window.destroy()

    def _validateStock(self, s=''):
        if len(s) == 6 and s.isdigit():
            return True
        else:
            self.write_log_to_Text("输入的股票代码无效！")
            return False

    def _validateRate(self, r=1.0):
        if r > 0.0 and r <= 5.0:
            return True
        else:
            self.write_log_to_Text("输入的比率%%无效（0.0~5.0）！")
            return False

    def _validateInterval(self, i=5):
        if i.isdigit() and i > 2 and i <= 60:
            return True
        else:
            self.write_log_to_Text("输入的监控间隔无效（3~60）！")
            return False

    def _validateOffset(self, n=1):
        if n.isdigit() and n >= 1 and n <= 5:
            return True
        else:
            self.write_log_to_Text("输入的周期数无效（1~5）！")
            return False

    def set_init_window(self):
        """设置窗口
        """
        self.init_window.title("盘中监控工具 v1.0")
        self.init_window.geometry("+500+20")
        self.init_window.resizable(0, 0)
        self.init_window.iconbitmap("tt.ico")

        # 实时监控部分
        realtimePart = LabelFrame(self.init_window, bd=2, text='实时监控')
        realtimePart.grid(row=0, column=0, padx=5, pady=5, sticky=N+S+W+E)
        Label(realtimePart, text="监控间隔(s)").grid(row=0, column=0, sticky=E)
        Entry(realtimePart, width=10, textvariable=self.intervalVar, validate="focusout",
              validatecommand=(self._validateInterval, '%s')).grid(row=0, column=1, sticky=W, padx=10, pady=10)
        Button(realtimePart, text="更新间隔", bg="lightblue", command=self.set_interval).grid(row=0, column=2, padx=10, pady=10, sticky=W)
        Label(realtimePart, text="股票代码").grid(row=1, column=0, sticky=E)
        Entry(realtimePart, width=10, textvariable=self.stock1Var, validate="focusout",
              validatecommand=(self._validateStock, '%s')).grid(row=1, column=1, sticky=W, padx=10, pady=10)
        Label(realtimePart, text="价格下限").grid(row=2, column=0, sticky=E)
        Entry(realtimePart, width=10, textvariable=self.lowerVar).grid(row=2, column=1, sticky=W, padx=10, pady=10)
        Label(realtimePart, text="价格上限").grid(row=3, column=0, sticky=E)
        Entry(realtimePart, width=10, textvariable=self.upperVar).grid(row=3, column=1, sticky=W, padx=10, pady=10)
        Label(realtimePart, text="价格浮动比率(%)").grid(row=4, column=0, sticky=E)
        Entry(realtimePart, width=10, textvariable=self.rate1Var, validate="focusout",
              validatecommand=(self._validateRate, '%s')).grid(row=4, column=1, sticky=W, padx=10, pady=10)
        Button(realtimePart, text="删除监控", bg="lightgray", command=self.delete_real_time_rule).grid(
            row=5, column=0, padx=10, pady=10, sticky=E)
        Button(realtimePart, width=10, text="添加监控", bg="lightblue", command=self.add_real_time_rule).grid(
            row=5, column=1, padx=10, pady=10, sticky=W)
        self.realtime_rule_LB = Listbox(realtimePart, selectmode=MULTIPLE, height=10, width=20)
        self.realtime_rule_LB.grid(row=1, rowspan=5, column=2, pady=10, sticky=W+E+N+S)
        scr1 = Scrollbar(realtimePart)
        self.realtime_rule_LB.config(yscrollcommand=scr1.set)
        scr1.config(command=self.realtime_rule_LB.yview)
        scr1.grid(row=1, rowspan=5, column=3, pady=10, sticky=N+S+W)

        # 周期MA监控部分
        periodPart = LabelFrame(self.init_window, bd=2, text='周期MA监控')
        periodPart.grid(row=1, column=0, padx=5, pady=5, sticky=N+S+W+E)
        Label(periodPart, text="股票代码").grid(row=1, column=0, sticky=E)
        Entry(periodPart, textvariable=self.stockVar, validate="focusout",
              validatecommand=(self._validateStock, '%s')).grid(row=1, column=1, sticky=W, columnspan=3, padx=10, pady=10)

        Label(periodPart, text="周期(m)").grid(row=2, column=0, sticky=E)
        Radiobutton(periodPart, text="15", variable=self.periodVar, value=15).grid(row=2, column=1, sticky=W, padx=10)
        Radiobutton(periodPart, text="30", variable=self.periodVar, value=30).grid(row=2, column=2, sticky=W, padx=10)
        Radiobutton(periodPart, text="60", variable=self.periodVar, value=60).grid(row=2, column=3,  sticky=W, padx=10)
        Radiobutton(periodPart, text="120", variable=self.periodVar, value=120).grid(row=3, column=1,  sticky=W, padx=10)
        Radiobutton(periodPart, text="240", variable=self.periodVar, value=240).grid(row=3, column=2, sticky=W, padx=10)

        Label(periodPart, text="MA值").grid(row=4, column=0, sticky=E)
        Radiobutton(periodPart, text="5", variable=self.maVar, value=5).grid(row=4, column=1, sticky=W, padx=10)
        Radiobutton(periodPart, text="10", variable=self.maVar, value=10).grid(row=4, column=2,  sticky=W, padx=10)
        Radiobutton(periodPart, text="20", variable=self.maVar, value=20).grid(row=4, column=3,  sticky=W, padx=10)
        Radiobutton(periodPart, text="30", variable=self.maVar, value=30).grid(row=5, column=1, sticky=W, padx=10)
        Radiobutton(periodPart, text="60", variable=self.maVar, value=60).grid(row=5, column=2, sticky=W,  padx=10)

        Button(periodPart, text="删除规则", bg="lightgray", command=self.delete_period_rule).grid(row=6, column=0, padx=10, pady=10, sticky=E)
        Button(periodPart, text="添加规则", bg="lightblue", command=self.add_period_rule).grid(row=6, column=1, padx=10, pady=10, sticky=W)

        self.period_rule_LB = Listbox(periodPart, selectmode=MULTIPLE, height=10, width=40)
        self.period_rule_LB.grid(row=7, column=0, columnspan=4, padx=15, ipadx=5, pady=10, sticky=W+E)
        scr = Scrollbar(periodPart)
        self.period_rule_LB.config(yscrollcommand=scr.set)
        scr.config(command=self.period_rule_LB.yview)
        scr.grid(row=7, column=3, pady=10, sticky=N+S+E)

        # 日志部分
        logPart = LabelFrame(self.init_window, bd=2, text='监控日志')
        logPart.grid(row=0, column=1, padx=5, pady=5, sticky=N+S+W+E)
        self.log_Text = ScrolledText(logPart, height=19, width=60)
        self.log_Text.grid(row=0, column=0, padx=10, pady=10, sticky=N+S+W+E)

        # 趋势MA监控部分
        trendPart = LabelFrame(self.init_window, bd=2, text='趋势MA监控')
        trendPart.grid(row=1, column=1, padx=5, pady=5, sticky=N+S+W+E)
        Label(trendPart, text="股票代码").grid(row=0, column=0, sticky=E)
        Entry(trendPart, textvariable=self.stock2Var, validate="focusout",
              validatecommand=(self._validateStock, '%s')).grid(row=0, column=1, sticky=W, columnspan=3, padx=10, pady=10)

        Label(trendPart, text="周期(m)").grid(row=1, column=0, sticky=E)
        Radiobutton(trendPart, text="120", variable=self.period2Var, value=120).grid(row=1, column=1,  sticky=W, padx=10)
        Radiobutton(trendPart, text="240", variable=self.period2Var, value=240).grid(row=1, column=2, sticky=W, padx=10)

        Label(trendPart, text="MA值").grid(row=2, column=0, sticky=E)
        Radiobutton(trendPart, text="5", variable=self.ma2Var, value=5).grid(row=2, column=1, sticky=W, padx=10)
        Radiobutton(trendPart, text="10", variable=self.ma2Var, value=10).grid(row=2, column=2,  sticky=W, padx=10)
        Radiobutton(trendPart, text="20", variable=self.ma2Var, value=20).grid(row=2, column=3,  sticky=W, padx=10)
        Radiobutton(trendPart, text="30", variable=self.ma2Var, value=30).grid(row=2, column=4, sticky=W, padx=10)
        Label(trendPart, text="浮动比率(%)").grid(row=3, column=0, sticky=E)
        Entry(trendPart, width=10, textvariable=self.rate2Var, validate="focusout",
              validatecommand=(self._validateRate, '%s')).grid(row=3, column=1, sticky=W, padx=10, pady=10)
        Label(trendPart, text="未来周期数").grid(row=3, column=2, sticky=E)
        Entry(trendPart, width=10, textvariable=self.offset2Var, validate="focusout",
              validatecommand=(self._validateOffset, '%s')).grid(row=3, column=3, sticky=W, padx=10, pady=10)

        Button(trendPart, text="删除规则", bg="lightgray", command=self.delete_trend_rule).grid(row=4, column=0, padx=10, pady=10, sticky=E)
        Button(trendPart, text="添加规则", bg="lightblue", command=self.add_trend_rule).grid(row=4, column=1, padx=10, pady=10, sticky=W)

        self.trend_rule_LB = Listbox(trendPart, selectmode=MULTIPLE, height=10, width=50)
        self.trend_rule_LB.grid(row=7, column=0, columnspan=5, padx=15, ipadx=5, pady=10, sticky=W+E)
        scr = Scrollbar(trendPart)
        self.trend_rule_LB.config(yscrollcommand=scr.set)
        scr.config(command=self.trend_rule_LB.yview)
        scr.grid(row=7, column=4, pady=10, sticky=N+S+E)

        # 启动监控器
        self.monitor = StockMonitor(log_f=self.write_log_to_Text)
        self.write_log_to_Text('Load rules from local configure file.')
        self.monitor.load_from_config()

        for i in range(0, len(self.monitor.period_rules)):
            l = self.monitor.period_rules.iloc[i].to_list()
            self.add_period_rule(*l, True)
        for i in range(0, len(self.monitor.realtime_rules)):
            l = self.monitor.realtime_rules.iloc[i].to_list()
            self.add_real_time_rule(*l, True)
        for i in range(0, len(self.monitor.trend_rules)):
            l = self.monitor.trend_rules.iloc[i].to_list()
            self.add_trend_rule(*l, True)

        self.monitor.run()

    def set_interval(self):
        """设置监控器的interval：3~60s
        """
        self.monitor.set_interval(self.intervalVar.get())

    def add_real_time_rule(self, stock='', lower=0.0, upper=0.0, rate=1.0, alert_times=0, from_config=False):
        """添加实时监控规则
        输入：stock,lower,upper,rate,alert_times
        """
        if from_config:  # 从配置文件载入，更新LB控件
            self.realtime_rule_LB.insert(END, '%s,%.3f,%.3f,%.1f' % (stock, lower, upper, rate))
        else:
            stock, rate, lower, upper = self.stock1Var.get(), self.rate1Var.get(), self.lowerVar.get(), self.upperVar.get()
            if self.monitor.add_real_time_rule(stock, lower, upper, rate):
                self.realtime_rule_LB.insert(END, '%s,%.3f,%.3f,%.1f' % (stock, lower, upper, rate))
            else:
                self.write_log_to_Text('The real-time rule already exists!', 'WARNING')

    def delete_real_time_rule(self):
        """删除实时监控规则"""
        if self.realtime_rule_LB.size() == 0:
            return

        for i in range(self.realtime_rule_LB.size()-1, -1, -1):
            if self.realtime_rule_LB.select_includes(i):
                self.monitor.delete_real_time_rule(i)
                self.realtime_rule_LB.delete(i)

    def add_period_rule(self, stock='', ma=0, period=0, from_config=False):
        """添加周期监控规则
        """
        if from_config:  # 从配置文件载入，更新LB控件
            self.period_rule_LB.insert(END, ' %s, %dm, MA%d' % (stock, period, ma))
        else:
            stock, ma, period = self.stockVar.get(), self.maVar.get(), self.periodVar.get()
            if self.monitor.add_period_rule(stock, ma, period):
                self.period_rule_LB.insert(END, ' %s, %dm, MA%d' % (stock, period, ma))
            else:
                self.write_log_to_Text("The period rule already exists!", 'WARNING')

    def delete_period_rule(self):
        """删除周期监控规则
        """
        if self.period_rule_LB.size() == 0:
            return

        for i in range(self.period_rule_LB.size()-1, -1, -1):
            if self.period_rule_LB.select_includes(i):
                self.monitor.delete_period_rule(i)
                self.period_rule_LB.delete(i)

    def add_trend_rule(self, stock='', ma=30, period=120, rate=5.0, offset=2, from_config=False):
        """添加趋势监控规则
        stock,MA,period,rate,offset
        """
        if from_config:  # 从配置文件载入，更新LB控件
            self.trend_rule_LB.insert(END, ' %s, %dm, MA%d, %.1f, %d' % (stock, period, ma, rate, offset))
        else:
            stock, ma, period, rate, offset = self.stock2Var.get(), self.ma2Var.get(), self.period2Var.get(), self.rate2Var.get(), self.offset2Var.get()
            if self.monitor.add_trend_rule(stock, ma, period, rate, offset):
                self.trend_rule_LB.insert(END, ' %s, %dm, MA%d, %.1f, %d' % (stock, period, ma, rate, offset))
            else:
                self.write_log_to_Text("The trend rule already exists!", 'WARNING')

    def delete_trend_rule(self):
        """删除趋势监控规则
        """
        if self.trend_rule_LB.size() == 0:
            return

        for i in range(self.trend_rule_LB.size()-1, -1, -1):
            if self.trend_rule_LB.select_includes(i):
                self.monitor.delete_trend_rule(i)
                self.trend_rule_LB.delete(i)

    def write_log_to_Text(self, logmsg, level='INFO'):
        """打印日志
        """
        global colors
        colors = {'INFO': 'gray', 'WARNING': 'orange',
                  'ALERT': 'blue', 'ERROR': 'red'}

        current_time = time.strftime('%H:%M:%S', time.localtime(time.time()))
        logmsg_in = str(current_time) + " " + str(logmsg) + "\n"  # 换行

        self.log_Text.tag_configure(level, foreground=colors[level])
        self.log_Text.insert(END, level+' ', level)

        self.log_Text.insert(END, logmsg_in)
        self.log_Text.update()
        self.log_Text.see(END)


def gui_start():
    BT_PORTAL = MY_GUI(Tk())
    BT_PORTAL.set_init_window()
    BT_PORTAL.init_window.mainloop()


gui_start()
